///@file Mesh.cpp
///@brief Implementation of the class representing a Finite Element mesh
///@author Arnaud Duval
///@version 0.0
///@date 2010/02/26

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef ENABLE_LIBXMLPP
#include <libxml++/libxml++.h>
#endif

#include <sstream>


#include "Mesh.h"

///@brief Default constructor
Mesh::Mesh()
{

}

///@brief Copy constructor
///@param m Mesh object to copy
Mesh::Mesh(const Mesh& m)
{
    nodes = m.nodes;
    materials = m.materials;
    elements = m.elements; 
}

///@brief destructor
Mesh::~Mesh()
{
    for(std::list<Element*>::iterator i = elements.begin(); i != elements.end() ; ++i)
    {
        if(*i)
        {
            delete *i;
            *i = NULL;
        }
    }

    for(std::list<Material*>::iterator i = materials.begin(); i != materials.end() ; ++i)
    {
        if(*i)
        {
            delete *i;
            *i = NULL;
        }
    }
}

///@brief Overload of = operator
///@param m Right hand operand
Mesh& Mesh::operator=(const Mesh m)
{
    nodes = m.nodes;
    materials = m.materials;
    elements = m.elements;
    return (*this);
}


#ifdef ENABLE_LIBXMLPP
///@brief Read mesh definition from xml file
void Mesh::ReadXML(std::string filepath)
{
	try
	{
		xmlpp::DomParser parser;
		parser.set_validate();
		parser.set_substitute_entities();	//We just want the text to be resolved/unescaped automatically
		parser.parse_file(filepath);
		if(parser)
		{
			//Walk the tree
			const xmlpp::Node* pNode = parser.get_document()->get_root_node();
			const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(pNode);
			const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(pNode);
			const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(pNode);

			Glib::ustring nodename = pNode->get_name();
			if(nodename == "mesh")	//root node corresponds to a mesh definition
			{
				xmlpp::Node::NodeList list = pNode->get_children();
				for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
				{
					xmlpp::Node* currentNode = dynamic_cast<const xmlpp::Node*>(*iter);
					if(currentNode->get_name() == "nodelist")
					///@brief Entering nodes list
					{
						ReadNodeListFromXMLNode(currentNode);
					}

					if(currentNode->get_name() == "matlist")
					///@brief Entering materials list
					{
						ReadMaterialListFromXMLNode(currentNode);
					}

					if(currentNode->get_name() == "elementlist")
					///@brief Entering elements list
					{
						ReadElementListFromXMLNode(currentNode);
					}
				}
			}
		}
	}
	catch(const std::exception& ex)
	{
		std::cout << "Exception caught: " << ex.what() << std::endl;
	}
}

///@brief Read the nodes list from a XML node
///@param currentNode Pointer to current node in XML tree. This node should correpsond to the node list definition
void Mesh::ReadNodeListFromXMLNode(xmlpp::Node* currentNode)
{
	xmlpp::Node::NodeList listNodes = currentNode->get_children();
	for(xmlpp::Node::NodeList::iterator iterNodes = listNodes.begin(); iterNodes != listNodes.end(); ++iterNodes)
	{
		xmlpp::Node* nodeToAdd = dynamic_cast<const xmlpp::Node*>(*iterNodes);
		if(nodeToAdd->get_name() == "node")
		///@brief Adding a new node to the mesh
		{
			Node NewNode;
			///@brief Obtaining attributes
			const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(nodeToAdd);
			const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
			for(xmlpp::Element::AttributeList::const_iterator iter_attr = attributes.begin(); iter_attr != attributes.end(); ++iter_attr)
			{
				const xmlpp::Attribute* attribute = *iter_attr;
				if(attribute->get_name() == "reference")
				{
					NewNode.Reference() = attribute->get_value();
				}
			}
			///@brief Obtaining coordinates
			const xmlpp::Node::NodeList list_coords = nodeToAdd->get_children();
			int dimcount = 0;
			for(xmlpp::Node::NodeList::const_iterator iterCoords = list_coords.begin(); iterCoords != list_coords.end(); ++iterCoords)
			{
				xmlpp::Node* isCoord = dynamic_cast<const xmlpp::Node*>(*iterCoords);
				///@brief Counting coordinates dimension
				if(isCoord->get_name() == "coord")
					dimcount++;
			}
			NewNode.SetDimension(dimcount);
			///@brief Setting node coordinates dimension
			dimcount = 0;
			for(xmlpp::Node::NodeList::const_iterator iterCoords = list_coords.begin(); iterCoords != list_coords.end(); ++iterCoords)
			{
				xmlpp::Node* isCoord = dynamic_cast<const xmlpp::Node*>(*iterCoords);
				if(isCoord->get_name() == "coord")
				{
					dimcount++;
					int coord_value;
					std::istringstream stream(((xmlpp::TextNode*)(*((isCoord->get_children()).begin())))->get_content());
					stream >> coord_value;
					if(stream.fail() == true)
					{
						std::cout << "Problem while reading node coordinate\n";
					}
					NewNode.SetCoordinate(dimcount, coord_value);
				}
			}
			nodes.push_back(NewNode);
		}
	}
}

///@brief Read the materials list from a XML node
///@param currentNode Pointer to current node in XML tree. This node should correpsond to the material list definition
void Mesh::ReadMaterialListFromXMLNode(xmlpp::Node* currentNode)
{
	xmlpp::Node::NodeList listMaterials = currentNode->get_children();
	for(xmlpp::Node::NodeList::iterator iterMaterials = listMaterials.begin(); iterMaterials != listMaterials.end(); ++iterMaterials)
	{
		xmlpp::Node* materialToAdd = dynamic_cast<const xmlpp::Node*>(*iterMaterials);
		if(materialToAdd->get_name() == "material")
		///@brief Adding a new material to the materials list
		{
			std::string ref_material;
			std::string type_material;
			///@brief Obtaining attributes
			const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(materialToAdd);
			const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
			for(xmlpp::Element::AttributeList::const_iterator iter_attr = attributes.begin(); iter_attr != attributes.end(); ++iter_attr)
			{
				const xmlpp::Attribute* attribute = *iter_attr;
				if(attribute->get_name() == "reference")
				{
					ref_material = attribute->get_value();
				}
				if(attribute->get_name() == "type")
				{
					type_material = attribute->get_value();
				}
			}
			Material *NewMaterial;
			if(type_material == "Elastic")
				NewMaterial = new ElasticMaterial();
			NewMaterial->SetReference(ref_material);
			///@brief Setting material parameters
			const xmlpp::Node::NodeList list_paramat = materialToAdd->get_children();
			int paramatcount = 0;
			for(xmlpp::Node::NodeList::const_iterator iterParamat = list_paramat.begin(); iterParamat != list_paramat.end(); ++iterParamat)
			{
				xmlpp::Node* isParamat = dynamic_cast<const xmlpp::Node*>(*iterParamat);
				if(isParamat->get_name() == "paramat")
				{
					paramatcount++;
					double paramat_value;
					std::istringstream stream(((xmlpp::TextNode*)(*((isParamat->get_children()).begin())))->get_content());
					stream >> paramat_value;
					if(stream.fail() == true)
					{
						std::cout << "Problem while reading material properties\n";
					}
										
					NewMaterial->SetParameter(paramatcount, paramat_value);
				}
			}
			materials.push_back(NewMaterial);
		}
	}
}

///@brief Read the elements list from a XML nodes
///@param currentNode Pointer to current node in XML tree. This node should correspond to the element list definition
void Mesh::ReadElementListFromXMLNode(xmlpp::Node* currentNode)
{
	xmlpp::Node::NodeList listNodes = currentNode->get_children();
	for(xmlpp::Node::NodeList::iterator iterNodes = listNodes.begin(); iterNodes != listNodes.end(); ++iterNodes)
	{
		xmlpp::Node* elementToAdd = dynamic_cast<const xmlpp::Node*>(*iterNodes);
		if(elementToAdd->get_name() == "element")
		///@brief Adding a new element to the mesh
		{
			std::string ref_element;
			std::string type_element;
			///@brief Obtaining attributes
			const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(elementToAdd);
			const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
			for(xmlpp::Element::AttributeList::const_iterator iter_attr = attributes.begin(); iter_attr != attributes.end(); ++iter_attr)
			{
				const xmlpp::Attribute* attribute = *iter_attr;
				if(attribute->get_name() == "reference")
				{
					ref_element = attribute->get_value();
				}
				if(attribute->get_name() == "type")
				{
					type_element = attribute->get_value();
				}
			}
			///@brief Obtaining Nodes of the elementlist
			const xmlpp::Node::NodeList list_nodes = elementToAdd->get_children();
			int nodecount = 0;
			for(xmlpp::Node::NodeList::const_iterator iterNodes = list_nodes.begin(); iterNodes != list_nodes.end(); ++iterNodes)
			{
				xmlpp::Node* isNodal = dynamic_cast<const xmlpp::Node*>(*iterNodes);
				if(isNodal->get_name() == "nodal")
					nodecount++;
			}
			std::string* nodesOfElement = new std::string[nodecount];
			nodecount = 0;
			for(xmlpp::Node::NodeList::const_iterator iterNodes = list_nodes.begin(); iterNodes != list_nodes.end(); ++iterNodes)
			{
				xmlpp::Node* isNodal = dynamic_cast<const xmlpp::Node*>(*iterNodes);
				if(isNodal->get_name() == "nodal")
				{
					nodesOfElement[nodecount] = ((xmlpp::TextNode*)(*((isNodal->get_children()).begin())))->get_content();
					nodecount++;
				}
			}
			///@brief searching nodes of element in Node list
			Node** nds = new Node*[nodecount];
			for(int i = 0 ; i < nodecount ; i++)
			{
				std::list<Node>::iterator j = nodes.begin();
				while((*j).Reference() != nodesOfElement[i])
                {
					j++;
                    if(j == nodes.end())
                    {
                      std::cerr << "ERROR : element " << nodesOfElement[i] << " is not referenced\n";
                      return;
                    }
                }
				nds[i] = &(*j);
			}
			std::string ref_mat;
			for(xmlpp::Node::NodeList::const_iterator iterNodes = list_nodes.begin(); iterNodes != list_nodes.end(); ++iterNodes)
			{
				xmlpp::Node* isRefMat = dynamic_cast<const xmlpp::Node*>(*iterNodes);
				if(isRefMat->get_name() == "matref")
				{
					ref_mat = ((xmlpp::TextNode*)(*((isRefMat->get_children()).begin())))->get_content();
				}
			}
			if(type_element == "Truss2")
			{
				Element *elt;
				elt = new Truss2(nds[0], nds[1]);
				elt->Reference() = ref_element;
				elt->Type() = type_element;
				elt->Update();
				///@brief Searching material reference in material list
				std::list<Material*>::iterator j = materials.begin();
				while((*j)->Reference() != ref_mat)
					j++;
				elt->SetMaterial(*j);
				elements.push_back(elt);
			}
			else if(type_element == "Quad4")
            {
                Element *elt;
                elt = new Quad4(nds[0], nds[1], nds[2], nds[3]);
                elt->Reference() = ref_element;
                elt->Type() = type_element;
                elt->Update();
                ///@brief Searching material reference in material list
                std::list<Material*>::iterator j = materials.begin();
                while((*j)->Reference() != ref_mat)
                    j++;
                elt->SetMaterial(*j);
                elements.push_back(elt);
            }
			delete[] nodesOfElement;
			delete[] nds;
		}
	}
}
#endif

///@brief Compute stiffness matrix
void Mesh::Equilibrate()
{
    //Computing the total number of DOF in the mesh
    unsigned int nDOF = 0;
    for(std::list<Node>::iterator lit = nodes.begin() ; lit != nodes.end() ; ++lit)
    {
        nDOF += (*lit).GetNumberOfDOF();
    }

    Matrix stiffness(nDOF, nDOF);
    stiffness.Nullify();
  
    ///@brief Compute for each element
	for(std::list<Element*>::iterator e = elements.begin() ; e != elements.end() ; ++e)
	{
		Vector a, b;
		Matrix c;
		(*e)->Equilibrate(a,b,c);
	}
}

///@brief Return true if the given node exists
///@param nodeName Name of the node to check
bool Mesh::NodeExists(std::string nodeName)
{
    std::list<Node>::iterator lit;
    for(lit = nodes.begin() ; lit != nodes.end() ; ++lit)
    {
        if(lit->Reference() == nodeName)
            return true;
    }
    return false;
}

///@brief Return a pointer to a given node
///@param nodeName Name of the node to point
Node* Mesh::GetPointerToNode(std::string nodeName)
{
    std::list<Node>::iterator lit;
    for(lit = nodes.begin() ; lit != nodes.end() ; ++lit)
    {
        if(lit->Reference() == nodeName)
            return &(*lit);
    }
    return NULL;
}



///@brief Overload of << operator
///@param s out stream
///@param m Mesh object to put in the out stream
std::ostream& operator<<(std::ostream& s, Mesh& m)
{
	s << "NODES :\n";
	for(std::list<Node>::iterator i = m.nodes.begin() ; i != m.nodes.end() ; ++i)
	{
		s << *i << "\n";
	}
	s << "MATERIALS :\n";
	for(std::list<Material*>::iterator i = m.materials.begin(); i != m.materials.end() ; ++i)
		s << **i << "\n";
	s << "ELEMENTS :\n";
	for(std::list<Element*>::iterator i = m.elements.begin(); i != m.elements.end() ; ++i)
	{
		s << **i << "\n";
	}
	return s;
}

